package io.gitee.zicai.graphql.sample.self;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import graphql.GraphQL;
import graphql.schema.DataFetcher;
import graphql.schema.GraphQLSchema;
import graphql.schema.idl.*;
import io.gitee.zicai.graphql.sample.self.fetcher.MajorDataFetcher;
import io.gitee.zicai.graphql.sample.self.fetcher.StudentDataFetcher;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Map;

/**
 * GraphQLProvider
 *
 * @author zicai
 * @since 2021-03-09
 */
@Slf4j
@Component
public class GraphQLProvider {

    private GraphQL graphQL;

    @Bean
    public GraphQL graphQL() {
        return graphQL;
    }

    @Autowired
    private StudentDataFetcher studentDataFetcher;
    @Autowired
    private MajorDataFetcher majorDataFetcher;

    @PostConstruct
    public void init() {
        GraphQLSchema graphQLSchema = buildSchema();
        this.graphQL = GraphQL.newGraphQL(graphQLSchema).build();
    }

    private GraphQLSchema buildSchema() {
        SchemaParser schemaParser = new SchemaParser();

        TypeDefinitionRegistry typeRegistry = new TypeDefinitionRegistry();
        loadSchema().stream().map(schemaParser::parse).forEach(typeRegistry::merge);

        RuntimeWiring runtimeWiring = buildWiring();
        SchemaGenerator schemaGenerator = new SchemaGenerator();
        return schemaGenerator.makeExecutableSchema(typeRegistry, runtimeWiring);
    }

    private List<File> loadSchema() {
        List<File> schemaFiles = Lists.newArrayList();
        try {
            ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
            Resource[] schemas = resourcePatternResolver.getResources(ResourceLoader.CLASSPATH_URL_PREFIX + "graphql/*.graphqls");
            for (Resource schema : schemas) {
                schemaFiles.add(schema.getFile());
            }
        } catch (IOException e) {
            log.error(">>> load graphql schema error -> ", e);
        }
        return schemaFiles;
    }

    private RuntimeWiring buildWiring() {
        //noinspection rawtypes
        Map<String, DataFetcher> queryMap = Maps.newHashMap();
        queryMap.put("queryStudentByName", studentDataFetcher.queryByName());
        queryMap.put("queryMajorByName", majorDataFetcher.queryByName());
        //noinspection rawtypes
        Map<String, DataFetcher> mutationMap = Maps.newHashMap();
        mutationMap.put("updateStudentById", studentDataFetcher.updateById());
        mutationMap.put("addStudent", studentDataFetcher.save());
        mutationMap.put("updateMajorById", majorDataFetcher.updateById());
        mutationMap.put("addMajor", majorDataFetcher.save());

        return RuntimeWiring.newRuntimeWiring()
                .type(TypeRuntimeWiring.newTypeWiring("Query").dataFetchers(queryMap))
                .type(TypeRuntimeWiring.newTypeWiring("Mutation").dataFetchers(mutationMap))
                .build();
    }
}
